Skip to content

feat(deploy): pull the released image in the NPM overlay instead of building#30

Merged
berntpopp merged 2 commits into
mainfrom
feat/deploy-released-image-in-npm-overlay
Jul 14, 2026
Merged

feat(deploy): pull the released image in the NPM overlay instead of building#30
berntpopp merged 2 commits into
mainfrom
feat/deploy-released-image-in-npm-overlay

Conversation

@berntpopp

Copy link
Copy Markdown
Owner

Problem

CI publishes an attested, digest-addressable image to ghcr.io/berntpopp/stringdb-link on
every release. The deployed compose chain never consumed it.

stringdb-link deploys as docker-compose.yml -f docker-compose.npm.yml, and the NPM file
is a pure overlay: it declares neither build: nor image:, so it inherited the base
file's build: block. Every deploy rebuilt the image from source on the server, and the
published, attested image was dead weight.

Confirmed on the VPS — stringdb_link_server is currently running
stringdb-link-npm-stringdb-link, a locally built image, not the GHCR release.

Change

docker/docker-compose.npm.yml now pulls the released image:

build: !reset null
image: ${STRINGDB_LINK_IMAGE:?Set STRINGDB_LINK_IMAGE to ghcr.io/berntpopp/stringdb-link@sha256:<digest>}
pull_policy: missing

build: !reset null is mandatory here. Unlike a self-contained overlay, adding only
image: would leave the base file's build: in the merged model and Compose would still
consider the service buildable. The pin fails closed when unset, matching the contract
docker-compose.prod.yml already uses. A digest is required rather than a tag: no latest
tag is published, and only a digest is immutable.

What deliberately did NOT change

  • container_name (stringdb_link_server) — NPM's proxy_pass resolves the container
    name
    , so renaming it would 502 the public host.
  • The Compose project name (stringdb-link-npm) — renaming it renames named volumes.
  • The healthcheck (path and start_period), the read-only rootfs / cap_drop: ALL
    hardening block, the networks and the command.

Validation

Rendered against the actual deployed chain:

$ docker compose -f docker/docker-compose.yml -f docker/docker-compose.npm.yml config
  required variable STRINGDB_LINK_IMAGE is missing a value      # fails closed

$ STRINGDB_LINK_IMAGE=...@sha256:... docker compose ... config
  name: stringdb-link-npm
  image: ghcr.io/berntpopp/stringdb-link@sha256:...
  container_name: stringdb_link_server
  pull_policy: missing
  # no `build:` key anywhere in the rendered model — the server cannot build

Also adds STRINGDB_LINK_IMAGE to .env.docker.example (it was documented nowhere an
operator would copy) and bumps to 4.0.6 to cut a release carrying the fix.

🤖 Generated with Claude Code

https://claude.ai/code/session_011Zn3DVmcCxo58ujjJjmmfa

berntpopp and others added 2 commits July 14, 2026 09:35
…uilding

CI publishes an attested, digest-addressable image to ghcr.io/berntpopp/stringdb-link
on every release. But the deployed compose chain never consumed it.

stringdb-link deploys as `docker-compose.yml -f docker-compose.npm.yml`, and the NPM
file is a PURE OVERLAY: it declares neither `build:` nor `image:`, so it inherited the
base file's `build:` block. Every deploy therefore rebuilt the image from source on the
server, and the published, attested image was dead weight. Confirmed on the VPS:
stringdb_link_server currently runs `stringdb-link-npm-stringdb-link` — a locally built
image, not the GHCR release.

The overlay now pulls the release:

    build: !reset null
    image: ${STRINGDB_LINK_IMAGE:?...}
    pull_policy: missing

`build: !reset null` is mandatory here. Unlike a self-contained overlay, adding only
`image:` would leave the base's `build:` in the merged model and Compose would still
consider the service buildable. It fails closed when the pin is unset, matching the
contract docker-compose.prod.yml already uses. A digest is required rather than a tag:
no `latest` tag is published, and only a digest is immutable.

Deliberately unchanged: `container_name` (stringdb_link_server — NPM's proxy_pass
resolves the container name, so renaming it would 502 the public host), the Compose
project name (stringdb-link-npm — renaming it would rename named volumes), the
healthcheck and its start_period, the read-only/cap-drop hardening block, the networks
and the `command`.

Verified by rendering the actual deployed chain:

    $ docker compose -f docker/docker-compose.yml -f docker/docker-compose.npm.yml config
      required variable STRINGDB_LINK_IMAGE is missing a value   # fails closed

    $ STRINGDB_LINK_IMAGE=...@sha256:... docker compose ... config
      image: ghcr.io/berntpopp/stringdb-link@sha256:...
      container_name: stringdb_link_server
      # no `build:` key anywhere in the rendered model — the server cannot build

Also adds STRINGDB_LINK_IMAGE to .env.docker.example (it was documented nowhere an
operator would copy) and bumps to 4.0.6 to cut a release carrying the fix.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011Zn3DVmcCxo58ujjJjmmfa
`uv.lock` records the project's own version, so bumping [project].version leaves it
stale. The container-CI gate runs `uv lock --check` and fails closed on it.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011Zn3DVmcCxo58ujjJjmmfa
@berntpopp
berntpopp merged commit 1f1e8f8 into main Jul 14, 2026
7 checks passed
@berntpopp
berntpopp deleted the feat/deploy-released-image-in-npm-overlay branch July 14, 2026 08:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant